home *** CD-ROM | disk | FTP | other *** search
/ Programming Sound Cards / Programming Sound Cards.iso / sound_53 / sbrecog.c < prev    next >
Text File  |  1995-01-01  |  10KB  |  403 lines

  1. /* A demonstration of how to use Kirstein's zero crossing interval
  2.    distributions to do a speaker dependent speech recognition of isolated
  3.    words, using a PC and a Sound-Blaster compatible sound card.
  4.  
  5.    Though I compiled it using Turbo-C 2.0, it should take little, if any,
  6.    adaptions to have a different compiler process it.
  7.  
  8.    Please read sbrecog.doc for further information.
  9.  
  10.    Johannes Kiehl, Trier (Germany), (c) 1993
  11. */
  12.  
  13. #define CPUSPEED 12
  14.     /* insert here the cpu tact rate in MHz */
  15.  
  16. #include <stdio.h>
  17. #include <stdlib.h>
  18. #include <alloc.h>
  19. #include <math.h>
  20. #include <io.h>
  21. #include <fcntl.h>
  22. #include <string.h>
  23.  
  24. #include <direct.h> /* SB direct access */
  25.  
  26. #define TRUE 1
  27. #define FALSE 0
  28. #define ERROR(x) {printf("\nAn error occured: "); printf(x); printf("\n");}
  29. #define MAXDICTIONARY 32
  30.     /* the maxinum number of dictionary entries.
  31.        Must keep it <256, or some variables will overflow
  32.     */
  33. #define PDELAY CPUSPEED*6
  34. #define RDELAY CPUSPEED*2.3
  35.  
  36. typedef int boole;
  37. typedef unsigned char byte;
  38. typedef float soundvect[16];
  39.  
  40. unsigned zerotable[64];
  41. soundvect dictionary[MAXDICTIONARY],parvect;
  42. char *identifiers[MAXDICTIONARY];
  43. int dictsize=0;
  44. unsigned zerolength=0;
  45.  
  46. void play_sample(byte *snd,long size)
  47. {       long i;
  48.     byte *wp1;
  49.  
  50.     wp1=snd;
  51.     speaker_on();
  52.     for(wp1=snd,i=0;i<size;i++,wp1++){
  53.         write_data(*wp1); asmdelay(PDELAY);
  54.     }
  55.     speaker_off();
  56. }
  57.  
  58. void clip(byte *signal,unsigned size)
  59. {    unsigned i;
  60.  
  61.     for (i=0;i<size;i++)
  62.         if ((byte)signal[i]>128) (byte)signal[i]=255;
  63.         else (byte)signal[i]=0;
  64. }
  65.  
  66. void classify(unsigned length)
  67. /* At a sampling rate of 11kHz, one byte represents 90 microseconds.
  68.    Thus 64 bytes mean an interval of 5.8 ms, or a 86 Hz frequency.
  69.    Fairly sufficient, according to Kristein, who set the lower
  70.    margin of his own implementation at 79 Hz (6.3 ms intervals).
  71.    This means that, for the given sampling rate, 64 classes instead
  72.    of Kristein's 200 are enough
  73. */
  74. {    zerotable[(length>64)?63:length-1]++;
  75. }
  76.  
  77. void analyze(byte *signal,unsigned size)
  78. {    unsigned i;
  79.  
  80.     clip(signal,size);
  81.     for (i=1;i<size;i++) {
  82.         zerolength++;
  83.         if ((byte)signal[i]!=(byte)signal[i-1]) {
  84.             classify(zerolength); zerolength=0;
  85.         }
  86.     }
  87. }
  88.  
  89. byte limits[17]={0,1,2,3,4,5,6,7,8,10,12,15,19,25,34,48,64};
  90.  
  91. void addtovector(byte i,float a)
  92. /*
  93.  table  1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 11 | 13 | 16 | 20 | 26 | 35 | 49
  94.           |   |   |   |   |   |   |   |-10|-12 |-15 |-19 |-25 |-34 |-48 |-64
  95.  class  1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16
  96. */
  97. {    byte j,size;
  98.  
  99.     j=i/4; while (i>limits[j]) j++;
  100.     size=limits[j]-limits[j-1];
  101.     parvect[j-1]+=(float)a/(float)size;
  102. }
  103.  
  104. void analyze_table(void)
  105. {    unsigned max=1;
  106.     byte i;
  107.     char j;
  108.     int h;
  109.  
  110.     for(i=0;i<64;i++) if (zerotable[i]>max) max=zerotable[i];
  111.     for(i=0;i<64;i++) addtovector(i+1,(float)zerotable[i]/(float)max);
  112. }
  113.  
  114. void tidyup(void)
  115. /* set the work space, i.e. parvect and zerotable, to all zeroes.
  116.    Must be done *before* any and every call to analyze
  117. */
  118. {    byte i;
  119.  
  120.     for(i=0;i<64;i++) {
  121.         parvect[i/4]=0.0;
  122.         zerotable[i]=0;
  123.     }
  124. }
  125.  
  126. boole harken(boole quiet)
  127. {       byte threshold=2;
  128.     byte *snd;
  129.     int ctr,w;
  130.     long i,size,maxsize=32768;
  131.     boole result=FALSE;
  132.  
  133.     tidyup();
  134.     if(reset_dsp()!=SBOK) {
  135.         printf("\nError resetting Sound Blaster.\n"); exit(1);
  136.     }
  137.     snd=(byte*)malloc(maxsize); ctr=0;
  138.     if (snd==(byte*)NULL) {
  139.         ERROR("Memory allocation (fct harken)"); exit(0);
  140.     }
  141.     do {
  142.         w=read_data(); asmdelay(RDELAY);
  143.         if (abs(w-128)>threshold) ctr++;
  144.         else ctr=0;
  145.     } while (ctr<16);
  146.     printf("Recording...");
  147.     ctr=i=0;
  148.     do {
  149.         w=read_data(); asmdelay(RDELAY);
  150.         if (abs(w-128)<threshold) ctr++;
  151.         else ctr=0;
  152.         snd[i++]=w;
  153.     } while ((ctr<512) && (i<maxsize));
  154.     printf(" Done (%u)\n",i);
  155.     i-=1024;
  156.     if (i>512) {
  157.         result=TRUE;
  158.                 if (!quiet) play_sample(snd,i);
  159.         analyze(snd+16,i);
  160.         analyze_table();
  161.     }
  162.     free(snd);
  163.     return(result);
  164. }
  165.  
  166. void training(void)
  167. {    char wk[64],wk2[8];
  168.     boole done,inok;
  169.     byte j,k=0;
  170.  
  171.     printf("You can now train up to %d words. You will be prompted to\n",
  172.         MAXDICTIONARY);
  173.     printf("  enter an ID string for each word, then pronounce it.\n");
  174.     do {
  175.         printf("\nPlease enter ID string #%d, or Q to quit training: ",
  176.             dictsize);
  177.         scanf("%s",wk);
  178.         done=!strcmp(wk,"Q");
  179.         if (!done) {
  180.             if (identifiers[dictsize]==NULL)
  181.                 identifiers[dictsize]=malloc(strlen(wk)+1);
  182.             strcpy(identifiers[dictsize],wk);
  183.             do {
  184.                 inok=harken(FALSE);
  185.                 if (!inok) {
  186.                     printf("No word identified. ");
  187.                     printf("Try again (Y/N)? Y ");
  188.                     scanf("%s",wk2);
  189.                     done=!strcmp(wk2,"N");
  190.                     if (done) inok=TRUE;
  191.                 }
  192.             } while (!inok);
  193.             if (!done) {
  194.                 printf("Insert %s into dictionary (Y/N)? Y ",
  195.                     wk);
  196.                 scanf("%s",wk2);
  197.                 if (!!strcmp(wk2,"N")) {
  198.                     for(j=0;j<16;j++)
  199.                         dictionary[dictsize][j]=
  200.                             parvect[j];
  201.                     dictsize++;
  202.                 }
  203.             }
  204.         }
  205.     } while (!done);
  206.     printf("\nWould you like another training set of the same words to\n");
  207.     printf("  be averaged with the set you just entered (Y/N)? N ");
  208.     scanf("%s",wk);
  209.     if (!!strcmp(wk,"N")) do {
  210.         printf("\nPlease speak again #%d: %s ",k,identifiers[k]);
  211.         do {
  212.             inok=harken(FALSE);
  213.             if (!inok) {
  214.                 printf("No word identified. ");
  215.                 printf("Please try again ");
  216.             }
  217.         } while (!inok);
  218.         printf("Average %s into dictionary (Y/N)? Y ",wk);
  219.         scanf("%s",wk2);
  220.         if (!!strcmp(wk2,"N")) {
  221.             for(j=0;j<16;j++) {
  222.                 dictionary[k][j]+=parvect[j];
  223.                 dictionary[k][j]/=2;
  224.             }
  225.             k++;
  226.         }
  227.     } while (k<dictsize);
  228. }
  229.  
  230. float contingency(byte n)
  231. /* compares the parameter vector (parvect) to the nth vector stored
  232.    in the dictionary. Returns a 0<=value<=1 (the "information trans-
  233.    mission rate" or "Transinformationswert") that serves as a measure
  234.    for the similarity of the two vectors. 0.0 means very similar.
  235.  
  236.    Error status: -1.0 -- All matrix fields zero, cannot divide
  237. */
  238. {    float s,t=0.0;
  239.     byte i,j;
  240.     float pij;
  241.     float cmatrix[3][17];
  242.     float result;
  243.  
  244.     /* copy parameter vectors into matrix, calculate line and
  245.        overall sums
  246.     */
  247.     cmatrix[2][16]=0.0;
  248.     for (i=0;i<2;i++) {
  249.         cmatrix[i][16]=0.0;
  250.         for (j=0;j<16;j++) {
  251.             if (i==0) cmatrix[i][j]=parvect[j];
  252.             else cmatrix[i][j]=dictionary[n][j];
  253.             cmatrix[i][16]+=cmatrix[i][j];
  254.         }
  255.         cmatrix[2][16]+=cmatrix[i][16];
  256.     }
  257.     if (cmatrix[2][16]==0.0) result=-1.0;
  258.     else {
  259.         /* normalize matrix to overall sum=1.0 */
  260.         for (i=0;i<2;i++)
  261.             for (j=0;j<17;j++)
  262.                 cmatrix[i][j]/=cmatrix[2][16];
  263.         cmatrix[2][16]=1.0;
  264.         /* calculate column sums */
  265.         for (j=0;j<16;j++) {
  266.             cmatrix[2][j]=0.0;
  267.             for (i=0;i<2;i++)
  268.                 cmatrix[2][j]+=cmatrix[i][j];
  269.         }
  270.         /* calculate rate of transmission */
  271.         for (i=0;i<2;i++)
  272.             for (j=0;j<16;j++) {
  273.                 s=cmatrix[i][16]*cmatrix[2][j];
  274.                 if (s>0.0) {
  275.                     pij=cmatrix[i][j];
  276.                     s=pij/s;
  277.                     if (s>0.0) t=t+pij*(log(s)/log(2));
  278.                 }
  279.             }
  280.         result=t;
  281.     }
  282.     return(result);
  283. }
  284.  
  285. typedef struct
  286. {
  287.     byte first[256],second[256];
  288. } matchstrc;
  289.  
  290. matchstrc *match(void)
  291. /* Compares the actual parvect to each one stored in the dictionary;
  292.    returns a pointer to the struct defined above. It contains two lists,
  293.    each shorter than 256 elements: The first list contains the closest
  294.    match or equally close matches. The second list contains elements
  295.    only if one or more matches were found whose contingency value is
  296.    "worse" by no more than 0.001.
  297.    The end of each list is indicated by a zero value! To make this
  298.    possible all the indices stored in the lists are >0; subtract 1
  299.    before using them.
  300.    Contingency values worse than 0.060 are not accepted as matches!
  301.  
  302.    Error status: NULL returned -- dictionary empty
  303. */
  304. {       byte i,cf,cs;
  305.     int rs,minval=2000;
  306.     int rsarr[MAXDICTIONARY];
  307.     matchstrc *result=NULL;
  308.  
  309.     for (i=0;i<dictsize;i++) {
  310.         rs=(int)(contingency(i)*1000);
  311.         if ((rs>-1000) && (rs<minval)) minval=rs;
  312.         rsarr[i]=rs;
  313.     }
  314.     if (minval<60) {
  315.         result=(matchstrc*)malloc(sizeof(matchstrc));
  316.         if (result!=NULL) {
  317.             cf=cs=0;
  318.             for (i=0;i<dictsize;i++) {
  319.                 if (rsarr[i]==minval)
  320.                     result->first[cf++]=i+1;
  321.                 if (rsarr[i]==minval+1)
  322.                     result->second[cs++]=i+1;
  323.             }
  324.             result->first[cf]=0;
  325.             result->second[cs]=0;
  326.         }
  327.     }
  328.     return(result);
  329. }
  330.  
  331. void ppmatches(matchstrc m)
  332. {    byte cf=0,cs=0,i;
  333.  
  334.     while (m.first[cf]>0) cf++;
  335.     while (m.second[cs]>0) cs++;
  336.     if (cf==1) printf("%s matched best. ",identifiers[m.first[0]-1]);
  337.     else {
  338.         for (i=0;i<cf;i++) {
  339.             printf("%s",identifiers[m.first[i]-1]);
  340.             if (i<cf-2) printf(", ");
  341.             else if (i<cf-1) printf(" or ");
  342.             else printf(" match best. ");
  343.         }
  344.     }
  345.     if (cs==1) printf("%s comes close",identifiers[m.second[0]-1]);
  346.     else {
  347.         for (i=0;i<cs;i++) {
  348.             printf("%s",identifiers[m.second[i]-1]);
  349.             if (i<cs-2) printf(", ");
  350.             else if (i<cs-1) printf(" and ");
  351.             else printf(" come close.");
  352.         }
  353.     }
  354.     printf("\n");
  355. }
  356.  
  357. void recogniser(void)
  358. {    char wk[8];
  359.     boole done,inok;
  360.     byte w;
  361.     matchstrc *matches;
  362.  
  363.     printf("\nNow you can speak words, the program will match them with\n");
  364.     printf("  the trained dictionary and produce the identifiers of\n");
  365.     printf("  the closest matches.\n");
  366.     do {
  367.         /*
  368.         printf("\nRecognise a word (Y/N)? Y ");
  369.         scanf("%s",wk);
  370.         done=!strcmp(wk,"N");
  371.         */
  372.         printf("\n"); done=FALSE;
  373.  
  374.         if (!done) {
  375.             if (harken(TRUE)) {
  376.                 matches=match();
  377.                 if (matches!=NULL) {
  378.                     ppmatches(*matches);
  379.                     free(matches);
  380.                 }
  381.                 else printf("No matches.\n");
  382.             }
  383.             else printf("No word identified during recording\n");
  384.         }
  385.     } while (!done);
  386. }
  387.  
  388. void initidents(void)
  389. {    int i;
  390.  
  391.     for(i=0;i<MAXDICTIONARY;i++)
  392.         identifiers[i]=NULL;
  393. }
  394.  
  395. main()
  396. {
  397.     printf("SBRECOG speech recogniser demo\n");
  398.     printf("(c) Johannes Kiehl, Trier 1993\n\n");
  399.     initidents();
  400.     training();
  401.     recogniser();
  402. }
  403.